<?php

/**
 * @author 	Samuel Carlier
 * @package P3Base_Socket
 */
abstract class P3Base_Socket_SMTP extends P3Base_Socket_Client {

	/**
	 * constants
	 */
	const AUTH_AUTO  = 'auto';
	const AUTH_LOGIN = 'login';
	const AUTH_PLAIN = 'plain';
	const AUTH_SUPPORTED = 'auto,login,plain';
	
	/**
	 * @var string
	 */
	private $_username;

	/**
	 * @var string
	 */
	private $_password;

	/**
	 * @var string
	 */
	private $_ehlo;

	/**
	 * @var string
	 */
	private $_auth_method;

	/**
	 * 
	 */
	protected function _connect() {
		parent::_connect();

		$this->_response(220);
		$this->_greet();

		$this->_doAuth();
	}
	
	/**
	 * check the response from ehlo for login method
	 */
	private function _doAuth() {

		if(empty($this->_username) || empty($this->_password)) {
			return;	
		}

		if($this->_auth_method == self::AUTH_AUTO) {
			$ehlo = explode("\n", $this->_ehlo);
			while(list(,$methods) = each($ehlo)) {
				if(strpos($methods, 'AUTH') !== false) {
					$methods = explode(' ', strtolower(trim(str_replace('250-AUTH ', '', $methods))));
					break;
				}
			}

			$this->_auth_method = $methods;
		}

		if(!is_array($this->_auth_method)) {
			$this->_auth_method = array($this->_auth_method);
		}
		
		$method = '';
		$supported = explode(',', self::AUTH_SUPPORTED);

		while(list(,$method) = each($supported)) {
			if(in_array($method, $this->_auth_method)) {
				break;
			}
		}

		if(empty($method)) {
			throw new P3Base_Socket_Exception('auth not supported ('. implode(', ', $this->_auth_method) .')');
		}

		$methodname = '_auth_' . $method;
		$this->$methodname();
	}

	/**
	 * @param string $username
	 * @param string $password
	 * @param int 	 $auth_method
	 */
	public function auth($username, $password, $auth_method=self::AUTH_AUTO) {
		$this->_username = $username;
		$this->_password = $password;

		if(!in_array($auth_method, explode(',', self::AUTH_SUPPORTED))) {
			throw new P3Base_Socket_Exception('Invalid auth_method given (' . $auth_method . ')');
		}
		
		$this->_auth_method = $auth_method;
	}

	/**
	 * Close the connection
	 */
	public function close() {
		if(is_null($this->_socket)) {
			return;	
		}

		try {
			$this->_write('QUIT');
			$this->_response(221, true);
				
		} catch(P3Base_Socket_Exception $e) {
			$catched_exception = $e;
		}

		parent::close();

		if(isset($catched_exception)) {
			throw $catched_exception;	
		}
	}
	
	/**
	 * HELO command
	 */
	protected function _greet() {
		$server = empty($_SERVER['SERVER_NAME']) ? 'localhost' : $_SERVER['SERVER_NAME'];

		$this->_write('EHLO ' . $server);
		$this->_ehlo = $this->_response(250, false, true);

		if($this->_secure === 'tls' && strpos($this->_ehlo, 'STARTTLS') !== false) {
			$this->_write('STARTTLS');
			$this->_response(220);
			
			$this->_enable_crypto(STREAM_CRYPTO_METHOD_TLS_CLIENT);
//			$this->_reset();
			
			$this->_write('EHLO ' . $server);
			$this->_ehlo = $this->_response(250, false, true);
		}
	}

	/**
	 * RSET command
	 */
	protected function _reset() {
		$this->_write('RSET');
		$this->_response(250);
	}

	/**
	 * MAIL command
	 *
	 * @param string $from
	 */
	protected function _setFrom($from) {
		$this->_write('MAIL FROM:' . $from);
		$this->_response(250);
	}

	/**
	 * RCPT command
	 *
	 * @param string $email_string
	 */
	protected function _setRCPT($email_string) {
		$this->_write('RCPT TO:' . $email_string);
		$this->_response(250);
	}

	/**
	 * start DATA command
	 *
	 * @param string $data
	 */
	protected function _startData() {
		$this->_write('DATA');
		$this->_response(354);
	}

	/**
	 * The content for DATA command
	 *
	 * @param string $data
	 * @param bool	 $line_ending
	 */
	protected function _writeData($data, $line_ending=true) {
		$this->_write($data, $line_ending);
	}

	/**
	 * end DATA command
	 */
	protected function _endData() {
		$this->_write(P3Base_Socket_Client::CRLF . '.');
		$this->_response(250);
	}

	/**
	 * Retrieve a response from the connected SMTP server
	 * if 4th charachter is a - we can assume
	 * that the server want's to tell us more
	 *
	 * @param  int		$expected_code
	 * @param  bool		$skipAutoClose
	 * @param  string	$return
	 * 
	 * @return string 	if $return is set true
	 */
	protected function _response($expected_code=0, $skipAutoClose=false, $return=false) {

		$read = '';
		while($response = $this->_read()) {
			$read .= $response;
			if($response{3} == " ") break;
		}

		$response_code = (int) substr($read, 0, 3);

		if($expected_code !== $response_code) {
			$message = substr($read, 4);
			
			if($skipAutoClose === false) {
				$this->close();
			}
			
			if(empty($read)) {
				throw new P3Base_Socket_Exception(P3Base_Language_Translator::get('empty_response'), 1);
			}

			throw new P3Base_Socket_Exception('code: ' . $response_code . "\n" . 'msg: ' . $message, 1);
		}
		
		if($return !== false) {
			return $read;
		}
	}

	/**
	 *
	 */
	private function _auth_plain() {
		$this->_write('AUTH PLAIN ' . base64_encode(chr(0) . $this->_username . chr(0) . $this->_password));
		$this->_response(235);
	}

	/**
	 *
	 */
	private function _auth_login() {
		$this->_write('AUTH LOGIN');
		$this->_response(334);
		
		$this->_write(base64_encode($this->_username));
		$this->_response(334);
		
		$this->_write(base64_encode($this->_password));
		$this->_response(235);
	}
}

?>
